package pers.sen.search.service.impl;

import com.alibaba.fastjson.JSON;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.common.unit.Fuzziness;
import org.elasticsearch.index.query.*;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import pers.sen.search.dto.Sku;
import pers.sen.search.entity.QueryPageBean;
import pers.sen.search.service.SearchService;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author Jason
 * @version 1.0.0
 * @date 2020/11/24 20:23
 * @Description TODO
 */
@Service
public class SearchServiceImpl implements SearchService {

    @Autowired
    private RestHighLevelClient client;


    @Override
    public Map searchGoods(QueryPageBean queryPageBean) throws Exception {
        // 构造关键字查询
        QueryBuilder queryBuilder = findKeyWord(queryPageBean);

        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        boolQueryBuilder.must(queryBuilder);

        // 构造过滤
        // 商品类别过滤
        queryBuilder = byCategoryNameFilter(boolQueryBuilder, queryPageBean);

        // 价格过滤
        queryBuilder = byPriceFilter(boolQueryBuilder, queryPageBean);

        return getResultByQuery(queryBuilder, queryPageBean);

    }


    // 关键字查询条件构造
    public QueryBuilder findKeyWord(QueryPageBean queryPageBean) throws Exception {

        String queryString = queryPageBean.getQueryString();
        if (!StringUtils.isEmpty(queryString)) {
            // 包含搜索关键字

            // 构造条件
            FuzzyQueryBuilder queryBuilder = QueryBuilders.fuzzyQuery("name", queryString).fuzziness(Fuzziness.ONE);
//            MatchQueryBuilder queryBuilder = QueryBuilders.matchQuery("name", queryString);

            // 分页查询
//            Map map = getResultByQuery(sourceBuilder.from(queryPageBean.getFrom()).size(queryPageBean.getPageSize()));

            return queryBuilder;
        } else {
            // 搜索关键字为null

            MatchAllQueryBuilder matchAllQueryBuilder = QueryBuilders.matchAllQuery();

            // 1.创建SearchSourceBuilder对象
            SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
            sourceBuilder.query(matchAllQueryBuilder);

//            Map map = getResultByQuery(sourceBuilder.from(queryPageBean.getFrom()).size(queryPageBean.getPageSize()));


            return matchAllQueryBuilder;
        }
    }

    // CategoryName商品类别过滤
    public QueryBuilder byCategoryNameFilter(BoolQueryBuilder boolQueryBuilder, QueryPageBean queryPageBean) {

        String categoryName = queryPageBean.getCategoryName();
        if (StringUtils.isEmpty(categoryName)) {
            return boolQueryBuilder;
        } else {
            TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("categoryName", categoryName);
            return boolQueryBuilder.filter(termQueryBuilder);
        }
    }

    // 价格过滤
    public QueryBuilder byPriceFilter(BoolQueryBuilder boolQueryBuilder, QueryPageBean queryPageBean) {
        Integer maxPrice = queryPageBean.getMaxPrice();
        Integer minPrice = queryPageBean.getMinPrice();
        RangeQueryBuilder rangeQueryBuilder = null;
        if (minPrice == 0 && maxPrice == -1) {
            return boolQueryBuilder;
        } else if (maxPrice == -1) {
            rangeQueryBuilder = QueryBuilders.rangeQuery("price").gte(minPrice);
        } else {
            rangeQueryBuilder = QueryBuilders.rangeQuery("price").gte(minPrice).lt(maxPrice);
        }
        return boolQueryBuilder.filter(rangeQueryBuilder);
    }


    // 分页条件构造
    public SearchSourceBuilder byPage(SearchSourceBuilder sourceBuilder, QueryPageBean queryPageBean) {
        int pageSize = queryPageBean.getPageSize();
        int currentPage = queryPageBean.getCurrentPage();
        int from = (currentPage - 1) * pageSize;
        return sourceBuilder.from(from).size(pageSize);
    }


    /**
     * 抽取通用构建查询条件执行查询方法
     *
     * @throws Exception
     */
    private Map getResultByQuery(QueryBuilder queryBuilder, QueryPageBean queryPageBean) throws Exception {
        //1. 创建SearchSourceBuilder对象
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();

        // 完全显示总条数
        sourceBuilder.trackTotalHits(true);

        //   ************ 构建查询条件************
        sourceBuilder.query(queryBuilder);
        // 构造分页
        sourceBuilder = byPage(sourceBuilder, queryPageBean);

        // 构造排序
        String createTimeSort = queryPageBean.getCreateTimeSort();
        switch (createTimeSort) {
            case "desc":
                sourceBuilder.sort("createTime", SortOrder.DESC);
                break;
            case "asc":
                sourceBuilder.sort("createTime", SortOrder.ASC);
                break;
            default:
                // 默认按照id升序
                sourceBuilder.sort("id", SortOrder.ASC);
                break;
        }


        // ***** 设置高亮三要素
        HighlightBuilder highlight = sourceBuilder.highlight()
                .field("name")
                .preTags("<font color='red'>")
                .postTags("</font>");// 高亮显示后缀

        sourceBuilder.highlighter(highlight);

        //2. 创建SearchRequest对象，并制定索引库名称
        SearchRequest request = new SearchRequest("sku");
        //3. 添加SearchSourceBuilder对象到SearchRequest对象source中
        request.source(sourceBuilder);


        //4. 发起请求，得到结果
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);

        //5. 解析结果SearchResponse
        SearchHits searchHits = response.getHits();
        //   1. 获取查询总条数
        long total = searchHits.getTotalHits().value;
        SearchHit[] hits = searchHits.getHits();

        List<Sku> result = new ArrayList<>();
        //   2. 获取SearchHits数组，并遍历
        for (SearchHit searchHit : hits) {
            //获取分数
            System.out.println("文档得分：" + searchHit.getScore());
            //      * 获取其中的`_source`，是JSON数据
            String skuJson = searchHit.getSourceAsString();
            //      * 把`_source`反序列化为User对象
            Sku sku = JSON.parseObject(skuJson, Sku.class);

            // 高亮name
            Map<String, HighlightField> highlightFields = searchHit.getHighlightFields();
            if (!(highlightFields == null || highlightFields.size() == 0)) {
                // ****** 解析高亮数据 searchHit.getHighlightFields()是一个存储高亮的map
                HighlightField highlightField = highlightFields.get("name"); // *** get("高亮显示域名称")
                // 获取的是一个高亮显示的片段数组
                Text[] fragments = highlightField.getFragments();
//                Text[] fragments = highlightField.fragments();

                // 拼接高亮片段
                String nameString = StringUtils.join(fragments);

                if (StringUtils.isNotBlank(nameString)) {
                    sku.setName(nameString);
                }
            }

            result.add(sku);
        }

//        // 获得查询的最后一个的排序数据
//        Object[] sortValues = hits[hits.length - 1].getSortValues();
//        // 存储下次查询使用
//        sourceBuilder.searchAfter(sortValues);
//        request.source(sourceBuilder);
//        // 发送请求继上一次查询
//        response = client.search(request, RequestOptions.DEFAULT);

        // 总页数计算
        int pageSize = queryPageBean.getPageSize();
        long temp = total / pageSize;
        long lastPage = total % pageSize == 0 ? temp : (temp + 1);


        Map<String, Object> map = new HashMap<>();
        map.put("lastPage", lastPage);
        map.put("total", total);
        map.put("result", result);
        System.out.println("map = " + map);
        return map;

    }
}
